﻿using SFML.Graphics;
using SFML.System;
using SFML.Window;

namespace WolfCarol.SFU
{
    public class Window
    {
        private static Window? _instance;

        public static Window Instance
        {
            get
            {
                if (_instance == null)
                {
                    throw new NullReferenceException("Instance of Window was null.");
                }

                return _instance;
            }
            private set
            {
                _instance = value;
            }
        }

        public WindowBase Base => _window;

        public float Width => _window.Size.X;

        public float Height => _window.Size.Y;

        private RenderWindow _window;

        private Clock _clock;

        private List<Entity> _entityList = new List<Entity>();

        private List<View> _viewList = new List<View>();

        public Window(uint width, uint height, string name)
        {
            if (_instance != null)
            {
                throw new InvalidOperationException("Multiple instances of Window are not allowed.");
            }

            _window = new RenderWindow(VideoMode.DesktopMode, name, Styles.Default);
            _window.Size = new Vector2u(width, height);
            _window.Resized += (_, e) =>
            {
                foreach (View view in _viewList)
                {
                    view.Size = new Vector2f(e.Width * view.Viewport.Width, e.Height * view.Viewport.Height);
                }
            };
            _window.Closed += (_, _) =>
            {
                OnDestroy();
                _window.Close();
            };
            _clock = new Clock();
            _instance = this;
        }

        public void AddEntity(Entity entity)
        {
            _entityList.Add(entity);
        }

        public void AddView(View view)
        {
            _viewList.Add(view);
        }

        public void Run()
        {
            while (_window.IsOpen)
            {
                Time.DeltaTime = _clock.Restart().AsSeconds();

                _window.DispatchEvents();

                foreach (Entity entity in _entityList)
                {
                    entity.Start();
                    entity.Update();
                }

                Input.UpdateInputStates();

                _window.Clear();

                IEnumerable<Renderer> rendererEntitys = from e in _entityList where e is Renderer select e as Renderer;
                IEnumerable<Renderer> renderers = from r in rendererEntitys orderby r.Order select r;

                foreach (View view in _viewList)
                {
                    _window.SetView(view);

                    foreach (Renderer renderer in renderers)
                    {
                        _window.Draw(renderer);
                    }
                }

                _window.Display();

                Debug.ShowFPS();
            }
        }

        public Vector2f MapPixelToCoords(Vector2i point, View view)
        {
            return _window.MapPixelToCoords(point, view);
        }

        public Vector2i MapCoordsToPixel(Vector2f point, View view)
        {
            return _window.MapCoordsToPixel(point, view);
        }

        public void SetFrameRateLimit(uint frameRate)
        {
            _window.SetFramerateLimit(frameRate);
        }

        private void OnDestroy()
        {
            foreach (Entity entity in _entityList)
            {
                entity.Destroy();
            }
        }
    }
}
